#include <assert.h>

#include "Application.h"
#include "DX.h"
#include "Window.h"
#include "View.h"
#include "DebugLog.h"
#include "MRect.h"

LRESULT CALLBACK defaultFunction( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch ( msg )
	{
		case WM_CLOSE:
		{
			PostQuitMessage( 0 );
			break;
		}
		default:
		{
			return DefWindowProc( hWnd, msg, wParam, lParam );
		}
	}

	return 0;
}

Window::Window( const WNDCLASSEX& wndClass, MRect rect, std::wstring title ) : 
																		_wndClass( wndClass ),
																		_contentView( 0 ), 
																		_rect( rect ), 
																		_title( title ), 
																		_hWnd( 0 ), 
																		prevHitView( 0 )
{
	init( wndClass, rect, title );
}

Window::Window( MRect rect, std::wstring title ) : 
													_contentView( 0 ), 
													_rect( rect ), 
													_title( title ), 
													_hWnd( 0 ), 
													prevHitView( 0 )
{
	//TRACEFUNC

	_wndClass.cbSize = sizeof( WNDCLASSEX );

	_wndClass.style			= CS_HREDRAW | CS_VREDRAW /*| CS_DBLCLKS*/;
	_wndClass.lpfnWndProc	= defaultFunction;
	_wndClass.cbClsExtra		= 0;
	_wndClass.cbWndExtra		= 0;
	_wndClass.hInstance		= Application::_()->hInstance;
	_wndClass.hIcon			= 0;
	_wndClass.hCursor		= LoadCursor( 0, IDC_ARROW );
	_wndClass.hbrBackground	= 0;
	_wndClass.lpszMenuName	= 0; // MAKEINTRESOURCE( IDR_MENU1 );
	_wndClass.lpszClassName	= title.c_str();
	_wndClass.hIconSm		= 0;

	init( _wndClass, rect, title );
}

Window::~Window( )
{
	//TRACEFUNC
	
	delete _contentView;
	_trackingViews.clear();
	DestroyWindow( _hWnd );
	UnregisterClass( _wndClass.lpszClassName, Application::_()->hInstance );
}

void Window::removeViewWithSubviewsFromHierarchy( View* view )
{
	assert( view );

	if ( contentView() != view )
	{
		View* super = view->superView();
		
		for ( size_t i = 0; i < view->countSubViews(); ++i )
		{
			removeViewWithSubViewsFromTrackingViews( view->subViewAtIndex( i ) );
		}

		super->removeSubView( view );
	}
}

void Window::addToTrackingViews( View* view )
{
	assert( view );

	_trackingViews.push_back( view );
}

void Window::removeViewFromTrackingViews( View* view )
{
	for ( std::vector< View* >::iterator i = _trackingViews.begin(); i != _trackingViews.end(); ++i )
	{
		if ( view == *i )
		{
			_trackingViews.erase( i );
			break;
		}
	}
}

void Window::removeViewWithSubViewsFromTrackingViews( View* view )
{
	removeViewFromTrackingViews( view );

	for ( std::vector< View* >::iterator i = _trackingViews.begin(); i != _trackingViews.end(); ++i )
	{
		removeViewWithSubViewsFromTrackingViews( *i );
	}
}

void Window::update( )
{
	contentView()->update();
}

//void Window::contentView( View* view )
//{
//	//TRACEFUNC
//	
//	if ( contentView() != view )
//	{
//		_contentView = view;
//
//		if ( contentView() )
//		{
//			contentView()->setWindow( this );
//			contentView()->setSuperView( 0 );
//			contentView()->setRect( getClientRect() );
//		}
//	}
//}

View* Window::contentView()
{	
	if ( !_contentView )
	{
		_contentView = new View( getClientRect() );
		_contentView->setWindow( this );
	}

	return _contentView;
}

void Window::setRect( MRect rect )
{
	// TODO setPosition
	_rect = rect;
}

MRect Window::rect()
{
	return _rect;
}

void Window::setTitle( std::wstring title )
{
	// TODO setTitle
	_title = title;
}

std::wstring Window::title()
{
	return _title;
}

HWND Window::hWnd()
{
	return _hWnd;
}

MRect Window::getClientRect()
{
	MRect rect;
	RECT clRect;
	
	GetClientRect( _hWnd, &clRect );
	
	rect.setX( ( float )clRect.left );
	rect.setY( ( float )clRect.top );
	rect.setWidth( ( float )( clRect.right - clRect.left ) );
	rect.setHeight( ( float )( clRect.bottom - clRect.top ) );
	
	return rect;
}

MPoint Window::clientToScreen( MPoint point )
{
	POINT p = { ( long )point.x(), ( long )point.y() };
	
	ClientToScreen( _hWnd, &p );
	
	point = MPoint( ( float )p.x, ( float )p.y );

	return point;
}

void Window::_trackView( const MPoint& point, View* view )
{
	// need to optimize
	// tracking
	for ( size_t i = 0; i < view->countSubViews(); ++i )
	{
		View* subView = view->subViewAtIndex( i );
		subView->track( subView->rect().isPointInRect( point ) );
		_trackView( point, subView );
	}
}

void Window::mouseMove( const IEvent& event ) 
{
	// tracking
	_trackView( event.point, contentView() );
	
	// draging
	if ( prevHitView )
	{
		if ( prevHitView && event.lPressed )
		{
			prevHitView->dragMouse( event );
		}
	}
}

void Window::wheelMove( const IEvent& event )
{
	View* hitView = contentView()->hitDistantView( event.point );

	if ( hitView )
	{
		hitView->wheelMove( event );
	}
}

void Window::leftMouseDown( const IEvent& event )
{
	prevHitView = contentView()->hitDistantView( event.point );

	if ( prevHitView )
	{
		//DEBUGTRACE(TEXT("leftMouseDown hitted view = "))
		//DEBUGTRACE(prevHitView->description())
		prevHitView->leftMouseDown( event );
	}
}

void Window::leftMouseUp( const IEvent& event ) 
{
	if ( prevHitView )
	{
		prevHitView->leftMouseUp( event );
	}

	prevHitView = 0;
}

void Window::clipCursor( MRect& rect )
{
	if ( rect.width() != 0 && rect.height() != 0 )
	{
		RECT rcClient;                 // client area rectangle 
		POINT ptClientUL;              // client upper left corner 
		POINT ptClientLR;
			
		SetCapture( _hWnd );
		//GetClientRect( _hWnd, &rcClient );
		SetRect( &rcClient, ( int )rect.x(), ( int )rect.y(), ( int )( rect.x() + rect.width() ), ( int )( rect.y() + rect.height() ) );

		ptClientUL.x = rcClient.left;
		ptClientUL.y = rcClient.top;
		ptClientLR.x = rcClient.right + 1; 
		ptClientLR.y = rcClient.bottom + 1;

		ClientToScreen( _hWnd, &ptClientUL); 
		ClientToScreen( _hWnd, &ptClientLR); 
		SetRect( &rcClient, ptClientUL.x, ptClientUL.y, ptClientLR.x, ptClientLR.y );
		ClipCursor( &rcClient );
	}
	else
	{
		ClipCursor( 0 ); 
        ReleaseCapture();
	}
}

#ifdef _DEBUG

void Window::dumpViews()
{
	//DEBUGLOGLN("DUMP VIEWS")
	//contentView()->dumpView();
}

#endif

void Window::init( WNDCLASSEX wndClass, MRect rect, std::wstring title )
{
	RegisterClassEx( &wndClass );

#if defined(FULLSCREEN)
	MONITORINFO monInfo;
	ZeroMemory( &monInfo, sizeof( MONITORINFO ) );
	monInfo.cbSize = sizeof( MONITORINFO );
	GetMonitorInfo( DX::_()->direct3D->GetAdapterMonitor( DX::_()->monitor ), &monInfo );

	_hWnd = CreateWindowEx( WS_EX_TOPMOST, wndClass.lpszClassName, _title.c_str(), WS_POPUP | WS_VISIBLE,
		0, 0, 0, 0, 0, 0, GetModuleHandle( 0 ), 0 );
	SetWindowPos( _hWnd, HWND_TOPMOST, 0, 0, monInfo.rcMonitor.right - monInfo.rcMonitor.right, monInfo.rcMonitor.bottom - monInfo.rcMonitor.top, SWP_ASYNCWINDOWPOS | SWP_NOCOPYBITS | SWP_NOSENDCHANGING );
#else
	_hWnd = CreateWindow( wndClass.lpszClassName, _title.c_str(), WS_OVERLAPPEDWINDOW,
		( int )rect.x(), ( int )rect.y(), ( unsigned )rect.width(), ( unsigned )rect.height(), 0, 0, wndClass.hInstance, 0 );
#endif
	if ( !_hWnd ) throw std::wstring( TEXT( "Window " ) + _title + std::wstring( TEXT( " doesn't created " ) ) );

	
}

void Window::showWindow()
{
	ShowWindow( _hWnd, SW_SHOWNORMAL );
	UpdateWindow( _hWnd );
}

void Window::close()
{
	PostQuitMessage( 0 );
}



